#include "get_col_names_from_header.h"

#include <iostream>
	using std::istream;
#include <string>
	using std::string;
#include <vector>
	using std::vector;
#include <boost/spirit/include/qi.hpp>
	namespace qi = boost::spirit::qi;
#include <stdexcept>
	using std::runtime_error;
#include <boost/range/algorithm.hpp>
	using boost::sort;
#include <boost/format.hpp>
	using boost::format;
#include <boost/lexical_cast.hpp>
	using boost::lexical_cast;

namespace parsing
{


void check_col_names_duplicates(vector<string> sorted_col_names, const string& file_name)
{
	//
	// check no duplicates of column names
	//
	boost::range::sort(sorted_col_names);
	for (unsigned ii = 0; ii < sorted_col_names.size() - 1; ++ii)
		if (sorted_col_names[ii] == sorted_col_names[ii+1])
			throw runtime_error((boost::format("Duplicate column names [%s] found in %s.") % sorted_col_names[ii] % file_name).str());

}


//!_______________________________________________________________________________________
//! 
//!		get_col_names_from_header
//!			
//! 
//!		\author lg (03/07/2011)
//!     
//!     \param input_stream 
//!     \param header_preamble 	   		Attempt to parse column names from lines beginning
//!     									with PREAMBLE
//!     \param col_names_preamble		Will try to parse col names from successive lines beginning 
//!											with PREAMBLE.
//!										If no header_preamble will throw after first non 
//!											matching line
//! 	\param cnt_expected_col_names 	If not zero, will throw exception if not match
//!     
//!     \return vector<string> 
//!_______________________________________________________________________________________
vector<string> get_col_names_from_header(	istream& 		input_stream, 
											const string&	file_name,
 											const string&	header_preamble, 
											const string&	col_names_preamble, 
											unsigned&		cnt_rows,
											unsigned		cnt_expected_col_names)
{
	using qi::graph;
	using qi::parse;
	using qi::eol;
	using qi::lit;

	std::string line;
	unsigned header_preamble_len 	= header_preamble.length();
	unsigned col_names_preamble_len	= col_names_preamble.length();
	while (input_stream)
	{
		++cnt_rows;
		getline(input_stream, line);
		if (!line.length())
			continue;

		// long enough for header (comment) line preamble (e.g. '#')
		if (line.length() < header_preamble_len)
			break;

		//
		//  Does not contain header (comment) line preamble (e.g. '#')
		//
		if (header_preamble.length() && line.compare(0, header_preamble_len, header_preamble) != 0)
			break;

		//
		//	Not header line with column names
		//
		if (col_names_preamble.length() && line.compare(header_preamble_len, 
														col_names_preamble_len, 
														col_names_preamble) != 0)
			// if lots of header lines (has header preamble), look for next header line
			// Otherwise should match first line
			if (!header_preamble.length())
				break;
			else
				continue;


		//
		//	Parse header line with column names
		//
		string::iterator beg =line.begin() + header_preamble_len + col_names_preamble_len;
		vector<string> col_names;
		if (!qi::parse(beg, line.end(), +qi::graph % qi::omit[qi::space], col_names))
			if (!header_preamble.length())
				break;
			else
				continue;


		//
		//	Check number of column names
		//
		if (cnt_expected_col_names)
		{
			if (col_names.size() != cnt_expected_col_names)
				throw runtime_error((boost::format("Expecting %d names in %s (%d found)")
										% cnt_expected_col_names % file_name % col_names.size()).str());
			check_col_names_duplicates(col_names, file_name);
		}
		return col_names;
	}

	if (!cnt_rows)
		throw parse_error((boost::format("Failed to parse column names "
										   "from (first) header line in [%s].") 
								% file_name).str());
	else
		throw parse_error((boost::format("Failed to parse column names "
										   "after first %d header lines in [%s].") 
								% cnt_rows	% file_name).str());

}
}
